package it.ap.sa.core.logback.config;

import java.nio.charset.Charset;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;

/**
 * @author Alessandro Paolillo
 */
@Service
public class LogbackBuilder {

	private static final Logger logger = LoggerFactory.getLogger(LogbackBuilder.class.getName());
	
	/**
	 * Build logger 
	 * 
	 * @param {@link String} nameLogger
	 * @param {@link Appender} appender
	 * @param {@link String} level, livello di log per logback. I valori possibili sono: INFO, DEBUG, WARN, ERROR. 
	 * @param {@link Boolean} additivity, true o false a seconda se il log prodotti dal logger devono essere scritto acnhe dal ROOT logger
	 * 
	 * @return {@link Logger}
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Logger buildLogger(String nameLogger, Appender appender, String level, Boolean additivity){
		try{
			/**
			 * Controllo che i parametri d'ingresso siano tutti valorizzati
			 */
			if(!StringUtils.hasText(nameLogger) || appender == null || level == null || additivity == null ){

				throw new RuntimeException("Build del Logger non ricuscito!! .. parametri obligatori non valorizzati.");
			}
			
			LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
			
			ch.qos.logback.classic.Logger logger = loggerContext.getLogger(nameLogger);
				logger.setAdditive(additivity);
				logger.setLevel(Level.toLevel(level));
				logger.addAppender(appender);

			return logger;
		}catch(Exception e){
			logger.error("buildLogger >>> " + e, e);
			throw(e);
		}
	}
	
	/**
	 * ConsoleAppender appends log events to <code>System.out</code> or
	 * <code>System.err</code> using a layout specified by the user. The default
	 * target is <code>System.out</code>.
	 * <p/>
	 * For more information about this appender, please refer to the online manual
	 * at http://logback.qos.ch/manual/appenders.html#ConsoleAppender
	 *
	 * @param {@link String} pattern
	 * @param {@link String} nameAppender
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Appender buildConsoleAppender(String pattern, String nameAppender){
		try{
			/**
			 * Controllo che i parametri d'ingresso siano tutti valorizzati
			 */
			if(!StringUtils.hasText(pattern) || !StringUtils.hasText(nameAppender)){

				throw new RuntimeException("Build del ConsoleAppender non ricuscito!! .. parametri obligatori non valorizzati.");
			}
			
			LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
			
			PatternLayoutEncoder encoder = new PatternLayoutEncoder();
				encoder.setContext(loggerContext);
				encoder.setPattern(pattern);
				encoder.setCharset(Charset.forName("UTF-8"));
				encoder.start();
	
			ConsoleAppender consoleAppender = new ConsoleAppender();
				consoleAppender.setContext(loggerContext);
				consoleAppender.setName(nameAppender);
				consoleAppender.setEncoder(encoder);
				consoleAppender.start();
				
			return consoleAppender;
		}catch(Exception e){
			logger.error("buildConsoleAppender >>> " + e, e);
			throw(e);
		}
	}
	
	/**
	 * {@link RollingFileAppender}
	 * RollingFileAppender extends FileAppender with the capability to rollover log files. For example, RollingFileAppender can log to a file named 
	 * log.txt file and, once a certain condition is met, change its logging target to another file.
	 * There are two important sub-components that interact with RollingFileAppender. The first RollingFileAppender sub-component, namely 
	 * RollingPolicy, (see below) is responsible for undertaking the actions required for a rollover. A second sub-component of RollingFileAppender, 
	 * namely TriggeringPolicy, (see below) will determine if and exactly when rollover occurs. Thus, RollingPolicy is responsible for the what and 
	 * TriggeringPolicy is responsible for the when.
	 * To be of any use, a RollingFileAppender must have both a RollingPolicy and a TriggeringPolicy set up. However, if its RollingPolicy also 
	 * implements the TriggeringPolicy interface, then only the former needs to be specified explicitly.
	 * 
	 * @param {@link String} name_appender
	 * @param {@link String} path_file, path della cartella dove saranno creati i file di log (ES: /home/log/) 
	 * @param {@link String} nome_file, nome del file di log senza estensione
	 * @param {@link String} filename_pattern,  politica di rollover (Es: %d{yyyy-MM-dd} --> rollover giornaliero) 
	 * @param {@link String} max_file_size, dimensione massima file di log
	 * @param {@link String} pattern, layout log
	 * 
	 * @return {@link RollingFileAppender}
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static RollingFileAppender buildRollingFileAppender(String name_appender, String path_file, String nome_file, String filename_pattern, String max_file_size, String pattern) {
		try{
			/**
			 * Controllo che i parametri d'ingresso siano tutti valorizzati
			 */
			if(!StringUtils.hasText(name_appender) || !StringUtils.hasText(path_file) || !StringUtils.hasText(nome_file) || 
					!StringUtils.hasText(filename_pattern) || !StringUtils.hasText(max_file_size) || !StringUtils.hasText(pattern)){

				throw new RuntimeException("Build del RollingFileAppender non ricuscito!! .. parametri obligatori non valorizzati.");
			}
			
			LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
			
			RollingFileAppender refAppender = new RollingFileAppender();
				refAppender.setContext(loggerContext);
				/** ha il nome del file attivo da scrivere. Se il file non esiste, viene creato **/
				refAppender.setFile(path_file + nome_file + ".log");
				/** **/
				refAppender.setName(name_appender);
			
			/**
			 * Esso definisce una politica di rollover in base al tempo, ad esempio per il giorno o per mese. 
			 * La proprietà fileNamePattern è obbligatoria perchè il periodo di rollover viene dedotto dal suo valore. 
			 * Il suo valore dovrebbe consistere nel nome del file, più un opportunamente collocato specificatore %d. 
			 * Lo specificatore %d può contenere un modello di data e ora, come specificato dalla classe java.text.SimpleDateFormat. 
			 * Se il modello di data e ora viene omesso, allora viene assunto per default il modello yyyy-MM-dd.
			 */
			TimeBasedRollingPolicy rollingPolicy = new TimeBasedRollingPolicy<>();
				rollingPolicy.setContext(loggerContext);
				/** Es: ${LOG_FILE}.%d{yyyy-MM-dd}.log ---> nome del file di log da archiviare + politica di rollover */
				rollingPolicy.setFileNamePattern(nome_file + filename_pattern + ".log");
				
				rollingPolicy.setParent(refAppender);
				/**
				 * La proprietà maxHistory opzionale controlla il numero massimo di file di archivio per mantenere, cancellando i file più vecchi. 
				 * Ad esempio, se si specifica rollover mensile, e s'imposta maxHistory a 6, verranno conservati i file meno vecchi di 6 mesi
				 * e cancellati quelli superiore ai 6 mesi. 
				 */
	//			rollingPolicy.setMaxHistory(0);
				/**
				 * If set to true, archive removal will be executed on appender start up. By default this property is set to false.
				 * Archive removal is normally performed during roll over. However, some applications may not live long enough for 
				 * roll over to be triggered. 
				 * It follows that for such short-lived applications archive removal may never get a chance to execute. 
				 * By setting cleanHistoryOnStart to true, archive removal is performed at appender start up.
				 */
				rollingPolicy.setCleanHistoryOnStart(false);
				/** start **/
				rollingPolicy.start();
				
			/**
			 * {@link SizeBasedTriggeringPolicy}
			 * SizeBasedTriggeringPolicy guarda alla dimensione del file attualmente attivo. Se cresce più grande della dimensione specificata, 
			 * si usa la RollingFileAppender per far scattare il rollover del file attivo esistente.
			 * SizeBasedTriggeringPolicy accetta solo un parametro, e cioè MaxFileSize, con un valore predefinito di 10 MB.
			 * L'opzione MaxFileSize può essere specificata in byte, kilobyte, megabyte o gigabyte scritto come: KB, MB e GB.
			 * Ad esempio, 5000000, 5000 KB, 5 MB e 2 GB sono tutti valori validi.
			 */
			SizeBasedTriggeringPolicy triggeringPolicy = new ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy();
				triggeringPolicy.setContext(loggerContext);
				triggeringPolicy.setMaxFileSize(max_file_size);
				triggeringPolicy.start();

			/** add appender **/
			refAppender.setRollingPolicy(rollingPolicy);
			refAppender.setTriggeringPolicy(triggeringPolicy);
				
			PatternLayoutEncoder encoder = new PatternLayoutEncoder();
				encoder.setContext(loggerContext);
				encoder.setPattern(pattern);
				encoder.setCharset(Charset.forName("UTF-8"));
				encoder.start();
			
			/** add appender **/
			refAppender.setEncoder(encoder);
			
			/** add logger **/
			refAppender.start();

			return refAppender;
		}catch(Exception e){
			logger.error("buildRollingFileAppender >>> " + e, e);
			throw(e);
		}
	}
}